﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using System.Web;
using NHibernate.Mapping;

namespace Apollo
{
    public class CoreModule : IModule
    {
        public const string ACTION_GEN_FORM_STRING = "GEN_FORM_STRING";
        public const string ACTION_GEN_LANG_DEFINITION = "GEN_LANG_DEFINITION";
        public const string ACTION_CLEAR_SESSION = "CLEAR_SESSION";
        public const string ACTION_SHARE_VIEW = "SHARE_VIEW";
        public const string ACTION_IMPORT_FROM_CSV = "IMPORT_FROM_CSV";

        public CoreModule() { }

        public ActionSubscription PrepareAction(string actionName, object obj)
        {
            //handle only Save and Delete
            if (actionName == Application.ACTION_DELETE_NAME)
                return new ActionSubscription(
                    this
                    , true
                    , ActionHandlingType.ActiveButAllowingPassive
                    , ActionSubscription.DEFAULT_PRIORITY);
            else if (actionName == Application.ACTION_SAVE_NAME)
                return this.PrepareEditAction();
            else
                return null;
        }

        public EditActionSubscription PrepareEditAction() {
            EditActionSubscription subscription = new EditActionSubscription(
                    this
                    , true
                    , ActionHandlingType.Passive
                    , ActionSubscription.DEFAULT_PRIORITY);

            // parse reuqest
            int entityId = 0;
            int entityEditId = 0;
            if (int.TryParse(Application.Current.Request["EditId"], out entityEditId))
            {
                subscription.Edit = Apollo.Application.Meta<EntityEdit>()
                            .SingleOrDefault<EntityEdit>(en => en.Id == entityEditId);
                subscription.Entity = subscription.Edit.Entity;
            }
            else if (int.TryParse(Application.Current.Request["EntityId"], out entityId))
            {
                Entity entity = Apollo.Application.Meta<Entity>()
                                .SingleOrDefault<Entity>(en => en.Id == entityId);

                subscription.Entity = entity;
                subscription.Edit = entity.DefaultEdit;
            }
            else
                return null;

            return subscription;
        }
        
        public bool IsAllowed(string actionName, Entity entity, object obj) {
            return true;
        }

        public ActionContext Save(object obj)
        {
            if(obj == null)
                return new ActionContext(new ActionMessage("Can not save null!", ActionMessageType.Error)) { State = ActionState.Failed };

            ActionContext context = new ActionContext();

            if (obj.GetType().GetInterface(typeof(IHistory<>).Name) != null && this.IsAnExistingEntity(obj))
            {
                if (Apollo.Application.Current.IsDirtyProperty(obj, "EffectiveDate") ||
                    Apollo.Application.Current.IsDirtyProperty(obj, "State"))
                {

                    System.Reflection.MethodInfo saveHistoryMethod = this.GetType()
                        .GetMethod("SaveHistory").MakeGenericMethod(obj.GetType());
                    ActionMessage hMess = (ActionMessage)saveHistoryMethod.Invoke(this, new object[1] { obj });

                    context.AddMessage(hMess);
                }
            } 

            Application.CurrentSession.Save(obj);

            context.AddMessage(new ActionMessage("Save successfull!"));
            
            return context;
        }

        public ActionMessage SaveHistory<T>(T obj) {
            // get the ID property
            Entity entity = Apollo.Application.Meta<Entity>()
                .SingleOrDefault<Entity>(e => e.Name == obj.GetType().Name);

            System.Reflection.PropertyInfo pi = obj.GetType().GetProperty(entity.IdentifierProperty);
            
            T history = (T)Apollo.Application.Current.GetOriginalEntity(obj);
            
            // reset the id
            if (pi.PropertyType == typeof(int)) 
                pi.SetValue(history, 0, null);
            else if (pi.PropertyType == typeof(string) )
                pi.SetValue(history, pi.GetValue(obj, null) + "H" + DateTime.Now.ToString("yyMMdd"), null);

            
            // set belong to and state
            ((IHistory<T>)history).BelongsTo = obj;

            // set learn date = now
            ((IHistory<T>)obj).LearnDate = DateTime.Now;

            Application.CurrentSession.Save(history);

            return new ActionMessage("History successfully saved!");
        }

        public ActionContext Delete(object obj)
        {
            Application.CurrentSession.Delete(obj);

            return new ActionContext(new ActionMessage("Delete successfull!"));
        }

        public ActionContext DoAction(string actionName, object obj)
        {
            return null;                
        }

        public void OnInit(Application app) {
            // register default actions
            app.RegisterAction(Application.ACTION_CREATE_NAME);
            app.RegisterAction(Application.ACTION_SAVE_NAME);
            app.RegisterAction(Application.ACTION_DELETE_NAME);
            app.RegisterAction(Application.ACTION_VIEW_NAME);
            app.RegisterAction(Application.ACTION_FILTER);
            app.RegisterAction(Application.ACTION_EXTRACOLUMNS);
            app.RegisterAction(Application.ACTION_SAVE_VIEW_NAME);

            // core actions
            app.RegisterAction(ACTION_GEN_FORM_STRING);
            app.RegisterAction(ACTION_GEN_LANG_DEFINITION);
            app.RegisterAction(ACTION_CLEAR_SESSION);
            app.RegisterAction(ACTION_SHARE_VIEW);
            app.RegisterAction(ACTION_IMPORT_FROM_CSV);
        }

        #region Actions

        [Action(ACTION_GEN_FORM_STRING, ActionHandlingType.Active)]
        public static ActionContext RedirectToEntityFormXMLGenerator(object obj) {
            HttpContext.Current.Response.Redirect("/GenFormString.aspx?EntityId=" + HttpContext.Current.Request["ObjectId"]);
            return null;
        }

        [Action(ACTION_IMPORT_FROM_CSV, ActionHandlingType.Active)]
        public static ActionContext RedirectToImportFromCSV(object obj) {
            HttpContext.Current.Response.Redirect("/BulkImportStep1.aspx");
            HttpContext.Current.Response.End();
            return null;
        }

        [Action(ACTION_SHARE_VIEW, ActionHandlingType.Active)]
        public static ActionContext RedirectToShareView(object obj)
        {
            HttpContext.Current.Response.Redirect("/ShareUserView.aspx?UserViewId=" + HttpContext.Current.Request["ObjectId"]);
            HttpContext.Current.Response.End();

            return null;
        }

        [Action(ACTION_GEN_LANG_DEFINITION, ActionHandlingType.Active)]
        public static ActionContext GenerateLanguageDefinition(object obj) {
            EntityProperty property = obj as EntityProperty;

            if (property == null)
                return new ActionContext(new ActionMessage("No property selected! Can not generate language definition!", ActionMessageType.Error));

            LanguageDefinition languageDefinition = Application.CurrentSession
                .QueryOver<LanguageDefinition>()
                .Where(ld => ld.Type == "EntityProperty" && ld.CodeHi == property.Entity.Name && ld.CodeLo == property.PropertyName && ld.Language.Id == Application.CurrentLanguage.Id)
                .SingleOrDefault();

            if (languageDefinition != null)
            {
                return new ActionContext(new ActionMessage("Language definition already present!", ActionMessageType.Warning));
            }
            else
            {
                languageDefinition = new LanguageDefinition()
                {
                    CodeHi = property.Entity.Name,
                    CodeLo = property.PropertyName,
                    Type = "EntityProperty",
                    DisplayName = property.PropertyName,
                    Language = Application.CurrentLanguage
                };

                Application.CurrentSession.Save(languageDefinition);

                return new ActionContext(new ActionMessage("Language definition generated!", ActionMessageType.Info));
            }
        }

        [Action(ACTION_CLEAR_SESSION, ActionHandlingType.Active)]
        public static ActionContext ClearSession(object obj) {
            Apollo.Application.ClearSession();
            return new ActionContext(new ActionMessage("Session cleared!", ActionMessageType.Info));
        }

        #endregion

        public bool IsAnExistingEntity(object obj) {
            string idPropertyName = Apollo.Application.Configuration.ClassMappings
                        .Single<PersistentClass>(p => p.MappedClass.Name == obj.GetType().Name).IdentifierProperty.Name;

            object idProperty = obj.GetType().GetProperty(idPropertyName).GetValue(obj, null);

            if (idProperty is int)
                return (int)idProperty > 0;
            else if (idProperty is string)
                return !string.IsNullOrEmpty((string)idProperty);
            else
                return false;
        }
    }
}
